// Copyright 2012 dmitry.a.novikov@gmail.com
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _INCLUDED_CNZ_DICTIONARYIMPL_H_
#define _INCLUDED_CNZ_DICTIONARYIMPL_H_

#include "dictionary.h"
#include "objectimpl.h"
#include <set>

using namespace std;

namespace CnsZ {

  class DictionaryItem {
  public:
    P<Object> identifer;
    P<Object> object;

    DictionaryItem() {}
    DictionaryItem(const Object* id) {
      identifer = id;
    }
    DictionaryItem(const Object* id, Object* obj) {
      identifer = id;
      object = obj;
    }

    inline bool operator < (const DictionaryItem& key) const {
      if(identifer==key.identifer || key.identifer.get()==0)
        return false;
      if(identifer.get()==0)
        return true;
      return (identifer->compare( key.identifer ) < 0);
    }
  
  };

  typedef std::set<DictionaryItem> DictionaryItems;

  class DictionaryIteratorImpl : public ObjectIterator {
    P<Object> dict; // for lock (addRef/release())
    DictionaryItems::iterator iterator;
    DictionaryItems::iterator begin;
    DictionaryItems::iterator end;
    mutable size_t cnt;
  public:

    ObjectIterator* init(const Object* dict, DictionaryItems& items, const Object* fromId, const Object* toId) {
      cnt = size_t(-1);
      this->dict = dict;
      if(fromId) {
        begin = items.lower_bound(DictionaryItem(fromId));
      }
      else {
        begin = items.begin();
      }
      if(toId) {
        end = items.lower_bound(DictionaryItem(toId));
      }
      else {
        end = items.end();
      }
      if(fromId && toId && end!=begin) {
        if(fromId->compare(toId)>0) {
          begin = end = items.end();
        }
      }
      iterator = begin;
      return this;
    }

    P<Object> identifer() const {
      if(done()) {
        throw Exception();
      }
      P<Object> res = iterator->identifer;
      return res;
    }

    P<Object> object() {
      if(done()) {
        throw Exception();
      }
      P<Object> res = iterator->object;
      return res;
    }

    P<Object> object() const {
      if(done()) {
        throw Exception();
      }
      P<Object> res = iterator->object;
      return res;
    }

    void next() {
      if(done()) {
        throw Exception();
      }
      P<Object> res = iterator->object;
      ++iterator;
    }

    bool done() const {
      return iterator==end;
    }

    size_t count() const {
      if(cnt==size_t(-1)) {
        cnt = 0;
        DictionaryItems::const_iterator iter = begin;
        while(iter!=end) {
          ++iter;
          ++cnt;
        }
      }
      return cnt;
    }

    void seekStart() {
      iterator = begin;
    }
  };

  template<class T=Dictionary>
  class DictionaryImpl : public T {
  protected:
    DictionaryItems items;
  public:

    DictionaryItem* find(const Object* id) {
      DictionaryItems::iterator it = items.find(DictionaryItem(id));
      P<Object> res;
      if(it!=items.end()) {
        return &(*it);
      }
      return 0;
    }

    void putAt(const Object* id, Object* object, P<Object>* replaced=0) {
      DictionaryItems::iterator it = items.find(DictionaryItem(id));
      if(it!=items.end()) {
        if(replaced) {
          *replaced = it->object;
        }
        it->object = object;
      }
      else {
        items.insert(DictionaryItem(id, object));
      }
    }

    size_t size() const {
      return items.size();
    }

    bool has(const Object* id) const {
      return (items.find(DictionaryItem(id))!=items.end());
    }

    P<Object> getAt(const Object* id) const {
      DictionaryItems::const_iterator it = items.find(id);
      if(it!=items.end()) {
        return it->object;
      }
      return P<Object>();
    }

    P<ObjectIterator> newIterator(const Object* dict, const Object* fromId, const Object* toId) const {
      return ImplRefCount<DictionaryIteratorImpl>::New()->init(dict, const_cast<DictionaryItems&>(items), fromId, toId);
    }

    P<ObjectIterator> newIterator(const Object* fromId, const Object* toId) const {
      return ImplRefCount<DictionaryIteratorImpl>::New()->init(0, const_cast<DictionaryItems&>(items), fromId, toId);
    }

    String toString() const {
      std::wostringstream s;
      P<ObjectIterator> iter = newIterator(0,0,0);
      s << size() << '{';
      while(!iter->done()) {
        s << iter->identifer() << ':' << iter->object();
        iter->next();
        if(!iter->done())
          s << ',';
      }
      s << '}';
      return String(s.str().data());
    }

  };

}

#endif //#ifndef _INCLUDED_CNZ_DICTIONARYIMPL_H_